package main

import (
	"fmt"
	"github.com/go-redis/redis"
	"time"
)

// 声明一个全局的rdb变量
var rdb *redis.Client

// 初始化连接
func initClient() (err error) {
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",  //密码
		DB:       0,   //要使用的数据库
		PoolSize: 100, //连接池的大小
	})

	_, err = rdb.Ping().Result()
	return err
}

func redisExample() {
	err := rdb.Set("score", 100, 0).Err()
	if err != nil {
		fmt.Printf("Set score failed, err:%v\n", err)
		return
	}

	val, err := rdb.Get("score").Result() //Result() 返回 val 和 err；Val() 只返回 val
	if err != nil {
		fmt.Printf("Get score failed, err:%v\n", err)
		return
	}
	fmt.Println("score", val)

	val2, err := rdb.Get("name").Result()
	if err == redis.Nil {
		fmt.Println("name does not exist")
	} else if err != nil {
		fmt.Printf("Get name failed, err:%v\n", err)
		return
	} else {
		fmt.Println("name", val2)
	}
}

func hGetDemo() {
	//HGetAll() 获取所有的字段
	v, err := rdb.HGetAll("user").Result()
	if err != nil {
		//redis.Nil
		//其他错误
		fmt.Printf("HGetAll failed, err:%v\n", err)
		return
	}
	fmt.Println(v)
	/*
		map[age:18 name:xyy]
	*/

	//HMGet() 获取指定的字段
	v2 := rdb.HMGet("user", "name", "age").Val()
	fmt.Println(v2)
	/*
		[xyy 18]
	*/

	//HGet() 获取一个字段
	v3 := rdb.HGet("user", "age").Val()
	fmt.Println(v3)
	/*
		18
	*/
}

func zSetDemo() {
	//key
	zSetKey := "language_rank"
	//value
	lanuages := []redis.Z{
		{Score: 90.0, Member: "Golang"},
		{Score: 98.0, Member: "Java"},
		{Score: 95.0, Member: "Python"},
		{Score: 97.0, Member: "JavaScript"},
		{Score: 99.0, Member: "C/C++"},
	}
	//ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	//defer cancel()

	//ZADD
	num, err := rdb.ZAdd(zSetKey, lanuages...).Result()
	if err != nil {
		fmt.Printf("ZAdd failed, err:%v\n", err)
		return
	}
	fmt.Printf("ZAdd %d succ.\n", num)

	fmt.Println("-------------------------------------------------------------------------------------------------")

	//把 Golang 的分数加 10
	newScore, err := rdb.ZIncrBy(zSetKey, 10.0, "Golang").Result()
	if err != nil {
		fmt.Printf("ZIncrBy failed, err:%v\n", err)
		return
	}
	fmt.Printf("Golang's score is %f now.\n", newScore)

	fmt.Println("-------------------------------------------------------------------------------------------------")

	//取分数最高的 3 个
	ret, err := rdb.ZRevRangeWithScores(zSetKey, 0, 2).Result()
	if err != nil {
		fmt.Printf("ZRevRange failed, err:%v\n", err)
		return
	}
	for _, z := range ret {
		fmt.Println(z.Member, z.Score)
	}

	fmt.Println("-------------------------------------------------------------------------------------------------")

	//取 95 ~ 100 分的
	op := redis.ZRangeBy{
		Min: "95",
		Max: "100",
	}
	ret, err = rdb.ZRevRangeByScoreWithScores(zSetKey, op).Result()
	if err != nil {
		fmt.Printf("ZRevRangeScore failed, err%v\n", err)
		return
	}
	for _, z := range ret {
		fmt.Println(z.Member, z.Score)
	}
}

// watchDemo 在 key 值不变的情况下将其值 + 1
func watchDemo(key string) error {
	return rdb.Watch(func(tx *redis.Tx) error {
		n, err := tx.Get(key).Int()
		if err != nil && err != redis.Nil {
			return err
		}
		//假设操作耗时 5 s
		//5 秒内我们通过其他的客户端修改 key，当前事务就会失败
		time.Sleep(5 * time.Second)
		_, err = tx.TxPipelined(func(pipeliner redis.Pipeliner) error {
			pipeliner.Set(key, n+1, time.Hour)
			return nil
		})
		return err
	}, key)
}

func main() {
	if err := initClient(); err != nil {
		fmt.Printf("init redis client failed, err:%v\n", err)
		return
	}
	fmt.Println("connect redis success...")
	//程序退出时释放相关资源
	defer rdb.Close()

	redisExample()

	hGetDemo()

	zSetDemo()

}
